/******************************************************************************/
/* File: tcpTrafficSender.cpp                                                 */
/******************************************************************************/

#include "tcpTrafficSender.h"


TCPTrafficSender::TCPTrafficSender(TCPSocket *pts, ThreadConf *ptc) 
                : pTcpSock(pts), pThreadConf(ptc), iSendRate(0) {

    pSendBuf = new char[DataStore::generalSetting.iTCPBufferLength];
    UtilClass::PatternBuffer(pSendBuf, DataStore::generalSetting.iTCPBufferLength);
}

TCPTrafficSender::~TCPTrafficSender() {
    if(pSendBuf) {
        delete pSendBuf;
        pSendBuf = NULL;
    }
}

void TCPTrafficSender::ExitCleanUp() {
    pThreadConf->SetStoppedFlag(1);
}

/*
//显然，通过延迟来实现速率控制是一种较为粗粒度的控制。一般而言，控制TCP速率可以依靠于令牌桶的方式。
 */
void TCPTrafficSender::Run() {
    
    std::cout << "Running tcp traffic sender instance....\n";
    
    int updateInterval = DataStore::generalSetting.iUpdateInterval;
    int reportInterval = DataStore::generalSetting.iReportInterval;
    int numOnce = DataStore::generalSetting.iNumOnce;
    int length = DataStore::generalSetting.iTCPBufferLength;
    
    /*
    // 设置发送超时标志，以妨一直阻塞在send上
    struct timeval sendTimeout;
    sendTimeout.tv_sec = 0;
    sendTimeout.tv_usec = updateInterval;
    
    try {
        pTcpSock->SetSockOpt(SOL_SOCKET, SO_SNDTIMEO, (char *)&sendTimeout, sizeof(sendTimeout));
    } 
    catch (SocketException se) {
        se.PrintException();
        ExitCleanUp();
        return;
    }
    */
    
    double startTime = 0.0;
    double endTime = 0.0;
    double bandwidth = 0.0;
    bool timeoutFlag = false;
    
    int64_t totBytes = 0, curBytes = 0;
    struct timeval lastPacketTime, lastReportTime, nowTime, reportTime, updateTime, timeoutTime;
    
    // tokens related.
    double time1 = 0.0;
    double time2 = 0.0;
    double tokens = 0.0;
    
    gettimeofday(&nowTime, NULL);
    time1 = nowTime.tv_sec + (nowTime.tv_usec / (double) MILLION);
    
    lastPacketTime = nowTime;
    lastReportTime = nowTime;
    reportTime = nowTime;
    UtilClass::TimevalAddLong(&reportTime, reportInterval);
    
    updateTime = nowTime;
    UtilClass::TimevalAddLong(&updateTime, updateInterval);
   
    timeoutTime = nowTime;
    if (pThreadConf->pFC->iTime > 0) {
        unsigned long iTOUsecs = pThreadConf->pFC->iTime * MILLION;
        UtilClass::TimevalAddLong(&timeoutTime, iTOUsecs);
        timeoutFlag = true;
    }
    
    pThreadConf->SetStartedFlag(1);
    
    iSendRate = pThreadConf->pFC->GetNextRate();
    /*
    循环内需要完成的流程：
    1 检查停止标志（iToStop标志）
    2 检查配置更新标志（iUpdate标志）
    3 更新令牌桶可用令牌数
    4 根据令牌数发送数据包，同时更新可用令牌数
    5 如果没有可用令牌，则选择延迟一小段时间
    6 统计数据
    7 检查是否需要Report
    8 检查是否需要更新速率（getNextRate）
    9 检查是否超时，超时则退出
     */
    while (true) {
        
        // 检查停止标志 --- 为了更好地解决，可以再做最后一次统计，然后再退出 
        if (pThreadConf->GetHaltFlag() == 1) {
            
            ExitCleanUp();
            return;
        }
        
        // 检查更新标志 --- 更新速率 -- 其余的后面再考虑
        if (pThreadConf->GetUpdateFlag() == 1)
            iSendRate = pThreadConf->pFC->GetNextRate();
        
        gettimeofday(&nowTime, NULL);
        time2 = nowTime.tv_sec + (nowTime.tv_usec / (double) MILLION);
        
        tokens += (time2 - time1) * iSendRate;
        time1 = time2;
        time2 = 0.0;
        
        if (tokens > 0) {
            int count = 0;
            curBytes = 0;
            while(count < numOnce) {
                try {
                    curBytes = pTcpSock->SendCompleteData(pSendBuf, length);
                    count++;
                } 
                catch (SocketException se) {
                    se.PrintException();
                    std::cout << "TCP traffic sender shutdown for remote disconnected.\n";
                    ExitCleanUp();
                    return;
                }
                totBytes += curBytes;
                tokens -= curBytes;
            }
        } 
        else {
            UtilClass::DelayLoop(10);
        }
        
        // 检查是否需要上传统计报告
        gettimeofday(&nowTime, NULL);
        if (UtilClass::TimevalGreaterThan(nowTime, reportTime)) {
            double diffUsecs = UtilClass::TimevalDiff(nowTime, lastReportTime);
            double factIntv = diffUsecs / MILLION;
            endTime += factIntv;
            bandwidth = totBytes / factIntv;
            UploadReport(startTime, endTime, totBytes, bandwidth);
            
            // 重置数据
            startTime = endTime;
            totBytes = 0;
            bandwidth = 0.0;
            
            // 重新设置reportTime
            reportTime = nowTime;
            UtilClass::TimevalAddLong(&reportTime, reportInterval);
            lastReportTime = nowTime;
        }
        
        // 检查是否需要更新速率（getNextRate）
        gettimeofday(&nowTime, NULL);
        if (UtilClass::TimevalGreaterThan(nowTime, updateTime)) {
            iSendRate = pThreadConf->pFC->GetNextRate();
            
            // 重新设置updateTime
            updateTime = nowTime;
            UtilClass::TimevalAddLong(&updateTime, updateInterval);
        }
        
        // 检查是否到时（mTime参数）
        if (timeoutFlag) {
            if (UtilClass::TimevalGreaterThan(nowTime, timeoutTime)) {
                std::cout << "TCP traffic sender timeout and exit.\n";
                ExitCleanUp();
                return; 
            }
        }
    }
}

void TCPTrafficSender::UploadReport(double begin, double end, int64_t transfer, double bandwidth) {
    std::cout << "Sender upload tcp report...\n";
    
    TcpReport *ptr = new TcpReport();
    ptr->fBeginTime = begin;
    ptr->fEndTime = end;
    ptr->fInterval = end - begin;
    ptr->iTransfer = transfer;
    ptr->fBandwidth = bandwidth;
     
    Report *pr = new Report();
    pr->iRole = 1; // -- Sender
    pr->iProto = 1; // -- TCP
    char *lip = pTcpSock->LocalHostName();
    pr->sSenderIP = strdup(lip);
    pr->iSenderPort = pTcpSock->LocalPortNumber();
    char *rip = pTcpSock->RemoteHostName();
    pr->sRecverIP = strdup(rip);
    pr->iRecverPort = pTcpSock->RemotePortNumber();
    pr->pTcpReport = ptr;
    
    Message *pm = new Message();
    pm->iMessageType = 3;
    pm->pReport = pr;
    
    pThreadConf->pMQ->PushMessage(pm);
}

/******************************************************************************/
/* End File: tcpTrafficSender.cpp                                             */
/******************************************************************************/